package deploy

import (
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/pkg/sftp"
	"golang.org/x/crypto/ssh"
)

type Config struct {
	Host        string
	User        string
	Password    string
	Path        string
	Name        string
	Bin         string
	SpvConfPath string
	SpvOutPath  string
}

func Run(cfg Config) error {
	// 1. SSH 连接
	fmt.Printf("### 正在连接远程服务器 host[%s] - user[%s] - password[%s]...\n", cfg.Host, cfg.User, cfg.Password)
	client, err := connectSSH(cfg)
	if err != nil {
		return fmt.Errorf("SSH连接失败: %v", err)
	}
	defer client.Close()
	fmt.Printf("### 远程服务器连接成功\n")

	// 2. 如果远程服务已开启，则先停止
	fmt.Printf("### 正在暂停[%s]服务...\n", cfg.Name)
	if err := runCommand(client, "supervisorctl stop "+cfg.Name); err != nil {
		fmt.Printf("[%s]服务不存在，跳过停止操作\n", cfg.Name)
		return fmt.Errorf("supervisor暂停服务[%s]失败: %v", cfg.Name, err)
	}
	fmt.Printf("### [%s]服务已暂停\n\n", cfg.Name)

	// 3. 上传二进制文件
	fmt.Printf("### [%s]服务文件上传中...\n", cfg.Name)
	remoteBinPath := filepath.Join(cfg.Path, cfg.Name)
	if err := uploadFile(client, cfg.Bin, remoteBinPath); err != nil {
		return fmt.Errorf("文件上传失败[%s]: %v", remoteBinPath, err)
	}
	fmt.Printf("### [%s]服务文件上传成功\n", cfg.Name)

	// 4. 生成 Supervisor 配置
	supervisorConf := generateSupervisorConfig(cfg)
	confPath := fmt.Sprintf("%s/%s.ini", cfg.SpvConfPath, cfg.Name)
	if err := createRemoteFile(client, confPath, supervisorConf); err != nil {
		return fmt.Errorf("supervisor配置[%s]创建失败,请检查supervisor配置目录: %v", confPath, err)
	}

	// 5. 重启 Supervisor
	fmt.Printf("### 正在启动服务[%s]...\n", cfg.Name)
	if err := runCommand(client, "supervisorctl start "+cfg.Name); err != nil {
		return fmt.Errorf("supervisor更新失败: %v", err)
	}
	fmt.Printf("### 服务[%s] 重启成功\n", cfg.Name)

	fmt.Printf("### ✅ 服务 [%s] 已成功部署到 %s:%s/%s\n", cfg.Name, cfg.Host, cfg.Path, cfg.Name)
	return nil
}

// SSH 连接
func connectSSH(cfg Config) (*ssh.Client, error) {
	config := &ssh.ClientConfig{
		User: cfg.User,
		Auth: []ssh.AuthMethod{
			ssh.Password(cfg.Password),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(), // 生产环境应验证HostKey
	}
	return ssh.Dial("tcp", cfg.Host+":22", config)
}

// 上传文件到远程
func uploadFile(client *ssh.Client, localPath, remotePath string) error {
	sftpClient, err := sftp.NewClient(client)
	if err != nil {
		return err
	}
	defer sftpClient.Close()

	// 创建远程目录
	remoteDir := filepath.Dir(remotePath)
	if err := sftpClient.MkdirAll(remoteDir); err != nil {
		return err
	}

	// 上传文件
	localFile, err := os.Open(localPath)
	if err != nil {
		return err
	}
	defer localFile.Close()

	remoteFile, err := sftpClient.Create(remotePath)
	if err != nil {
		return err
	}
	defer remoteFile.Close()

	if _, err := remoteFile.ReadFrom(localFile); err != nil {
		return err
	}

	// 设置可执行权限
	return sftpClient.Chmod(remotePath, 0755)
}

// 生成 Supervisor 配置模板
func generateSupervisorConfig(cfg Config) string {
	return fmt.Sprintf(`
[program:%s]
command=%s/%s
directory=%s
autostart=true
autorestart=true
stdout_logfile=%s/%s.out.log
redirect_stderr=true
user=root
`, cfg.Name, cfg.Path, cfg.Name, cfg.Path, cfg.SpvOutPath, cfg.Name)
}

// 在远程创建文件
func createRemoteFile(client *ssh.Client, path, content string) error {
	session, err := client.NewSession()
	if err != nil {
		return err
	}
	defer session.Close()

	// 同时捕获 stdout 和 stderr
	var stdout, stderr bytes.Buffer
	session.Stdout = &stdout
	session.Stderr = &stderr

	// 执行命令
	err = session.Run(fmt.Sprintf("echo '%s' > %s", content, path))

	// 如果命令执行失败
	if err != nil {
		// 组合所有可能的错误信息
		fullErr := fmt.Sprintf("exit status: %v\nstdout: %s\nstderr: %s",
			err, stdout.String(), stderr.String())
		return fmt.Errorf("%v", fullErr)
	}
	return nil
}

// 执行远程命令
func runCommand(client *ssh.Client, cmd string) error {
	session, err := client.NewSession()
	if err != nil {
		return err
	}
	defer session.Close()

	// 同时捕获 stdout 和 stderr
	var stdout, stderr bytes.Buffer
	session.Stdout = &stdout
	session.Stderr = &stderr

	// 执行命令
	err = session.Run(cmd)

	// 如果命令执行失败
	if err != nil {
		// 组合所有可能的错误信息
		fullErr := fmt.Sprintf("exit status: %v\nstdout: %s\nstderr: %s",
			err, stdout.String(), stderr.String())

		// 检查是否是"服务不存在"的错误
		if isNoSuchProcessError(fmt.Errorf("%v", fullErr)) {
			return nil
		}
		return fmt.Errorf("%v", fullErr)
	}
	return nil
}

// 判断是否是 "no such process" 错误
func isNoSuchProcessError(err error) bool {
	return strings.Contains(err.Error(), "no such process") ||
		strings.Contains(err.Error(), "ERROR (no such process)") ||
		strings.Contains(err.Error(), "not running") ||
		strings.Contains(err.Error(), "not found")
}
